#include "W25Q64.h"
/*
MCU         FLASH
PC0         CS
PA5         CLK
PA6(MISO)   D0
PA7(MOSI)  DIO
*/

#define CS PCout(0)
#define CLK PAout(5)
#define MOSI PAout(7)
#define MISO PAout(6)

void W25Q64_Init(void)
{
    //初始化两个时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    //将时钟引脚和据脚配置为推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5  | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    //PC0为片选引脚,设置同上
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_Init(GPIOC, &GPIO_InitStructure)

    //初始化数据引脚
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;    //上拉输入
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    CS = 1;
    CLK = 0;
    MISO = 1;

}

// //////////////////////////////////

/*
    函数功能：SPI发送一个字节数据
    入口参数：
        val：要发送的数据
    返回值：无
*/
void SPI_SendByte(uint8_t val)
{
    uint8_t i;
    //时钟拉低
    CLK = 0;
    //发送数据
    for(i=0;i<8;i++)
    {
        if(val&0x80)
            MOSI = 1;
        else
            MOSI = 0;
        val <<= 1;   //左移一位
        //时钟拉高
        CLK = 1;
        //时钟拉低
        CLK = 0;
    }
    //等待数据被接收
    while(MISO)
        ;
}

/*
    函数功能：SPI发送一个命令
    入口参数：
        cmd：要发送的命令
    返回值：无
*/
void SPI_SendCmd(uint8_t cmd)
{
    CS = 0;
    SPI_SendByte(cmd);
    CS = 1;
}


/*
    函数功能：SPI发送一个地址
    入口参数：
        addr：要发送的地址
    返回值：无
*/
void SPI_SendAddr(uint32_t addr)
{
    uint8_t i;
    //时钟拉低
    CLK = 0;
    //发送地址
    for(i=0;i<24;i++)
    {
        if(addr&0x800000)
            MOSI = 1;
        else
            MOSI = 0;
        addr <<= 1;   //左移一位
        //时钟拉高
        CLK = 1;
        //时钟拉低
        CLK = 0;
    }
    //等待数据被接收
    while(MISO)
        ;
}

/*
    函数功能：SPI发送一个数据
    入口参数：
        data：要发送的数据
    返回值：无
*/
void SPI_SendData(uint8_t data)
{
    uint8_t i;
    //时钟拉低
    CLK = 0;
    //发送数据
    for(i=0;i<8;i++)
    {
        if(data&0x80)
            MOSI = 1;
        else
            MOSI = 0;
        data <<= 1;   //左移一位
        //时钟拉高
        CLK = 1;
        //时钟拉低
        CLK = 0;
    }
    //等待数据被接收
    while(MISO)
        ;
}

/*
    函数功能：SPI接收一个字节数据
    入口参数：无
    返回值：接收到的数据
*/
uint8_t SPI_RecvByte(void)
{
    uint8_t i,val=0;
    //时钟拉低
    CLK = 0;
    //接收数据
    for(i=0;i<8;i++)
    {
        val <<= 1;   //左移一位
        if(MISO)
            val |= 0x01;
        //时钟拉高
        CLK = 1;
        //时钟拉低
        CLK = 0;
    }
    //等待数据被接收
    while(MISO)
        ;
    return val;
}

/*
    函数功能：SPI接收一个数据
    入口参数：无
    返回值：接收到的数据
*/
uint8_t SPI_RecvData(void)
{
    uint8_t i,val=0;
    //时钟拉低
    CLK = 0;
    //接收数据
    for(i=0;i<8;i++)
    {
        val <<= 1;   //左移一位
        if(MISO)
            val |= 0x01;
        //时钟拉高
        CLK = 1;
        //时钟拉低
        CLK = 0;
    }
    //等待数据被接收
    while(MISO)
        ;
    return val;
}

/*
    函数功能：W25Q64读取状态寄存器
    入口参数：无
    返回值：状态寄存器的值
*/
uint8_t W25Q64_ReadStatusReg(void)
{
    uint8_t val;
    //发送读取状态寄存器命令
    SPI_SendCmd(0x05);
    //接收状态寄存器的值
    val = SPI_RecvData();
    return val;
}

/*
    函数功能：W25Q64写状态寄存器
    入口参数：
        val：要写入的值
    返回值：无
*/
void W25Q64_WriteStatusReg(uint8_t val)
{
    //发送写状态寄存器命令
    SPI_SendCmd(0x01);
    //发送状态寄存器的值
    SPI_SendData(val);
}

/*
    函数功能：W25Q64读取数据
    入口参数：
        addr：要读取的地址
    返回值：读取到的数据
*/
uint8_t W25Q64_ReadData(uint32_t addr)     //读取数据
{
    uint8_t val;
    //发送读取数据命令
    SPI_SendCmd(W25Q64_ReadData);
    //发送地址
    SPI_SendAddr(addr);
    //接收数据
    val = SPI_RecvData();
    return val;
}

/*
    函数功能：W25Q64写入数据
    入口参数：
        addr：要写入的地址
        data：要写入的数据
    返回值：无
*/
void W25Q64_WriteData(uint32_t addr, uint8_t data)     //写入数据
{
    //发送写数据命令
    SPI_SendCmd(0x02);
    //发送地址
    SPI_SendAddr(addr);
    //发送数据
    SPI_SendData(data);
}

/*
    函数功能：W25Q64擦除扇区
    入口参数：
        addr：要擦除的扇区地址
    返回值：无
*/
void W25Q64_EraseSector(uint32_t addr)     //擦除扇区
{
    //发送擦除扇区命令  0xD8
    SPI_SendCmd(0xD8);
    //发送地址
    SPI_SendAddr(addr);
    //等待擦除完成
    while(W25Q64_ReadStatusReg()&0x01)
        ;
}

/*
    函数功能：W25Q64读取扇区数据
    入口参数：
        addr：要读取的扇区地址
        data：存放读取到的数据的指针
    返回值：无
*/
void W25Q64_ReadSector(uint32_t addr, uint8_t *data)     //读取扇区数据
{
    uint32_t i;
    //发送读取扇区命令  0x03
    SPI_SendCmd(0x03);
    //发送地址
    SPI_SendAddr(addr);
    //接收数据
    for(i=0;i<W25Q64_SECTOR_SIZE;i++)
    {
        *data++ = SPI_RecvData();
    }
}

/*
    函数功能：W25Q64写入扇区数据
    入口参数：
        addr：要写入的扇区地址
        data：存放要写入的数据的指针
    返回值：无
*/
void W25Q64_WriteSector(uint32_t addr, uint8_t *data)     //写入扇区数据
{
    uint32_t i;
    //发送写扇区命令  0x02
    SPI_SendCmd(0x02);
    //发送地址
    SPI_SendAddr(addr);
    //发送数据
    for(i=0;i<W25Q64_SECTOR_SIZE;i++)
    {
        SPI_SendData(*data++);
    }
    //等待写入完成
    while(W25Q64_ReadStatusReg()&0x01)
        ;
}